<html>
	<head>
		<!--script src="http://documentcloud.github.com/underscore/underscore.js"></script-->
		<script src="https://raw.github.com/DmitryBaranovskiy/raphael/master/raphael.js"></script>
		<!--script src="http://ajax.googleapis.com/ajax/libs/dojo/1.7.2/dojo/dojo.js"></script-->
		<script src="http://raphaeljs.com/plugins/raphael.path.methods.js" type="text/javascript" charset="utf-8"></script>
		
		<style>
			body{
				background-color:#000;
			}
			#canvas{
				background-color:#FFF;
			}
			
			td{
				vertical-align: top;
			}	
		</style>	
		
		<script>
			$ = function(id) {return document.getElementById(id);};
			forEach = function(seq,func){
				for (var i = 0; i < seq.length; i++){
					func(seq[i], i);
				}
			}
			cont = function (val, seq){
				for (var i = 0; i < seq.length; i++){
					if (val == seq[i]) return true;
				}
				return false;
			}

			getByPoint = function(map, point){
				return map[point.x][point.y];
			}	

			setByPoint = function(map, point, value){
				map[point.x][point.y] = value;
			}

			isInBounds = function(map, point){
				return point.x > -1 && point.y > -1 && point.x < map.length && point.y < map[point.x].length; 
			}	

			pi=Math.PI;	
		</script>
		<script>
			var minimap, canvas;
			
			function createMap(sx, sy){
				var map = [];
				for (var i = 0; i < sx; i++){
					var line = [];
					for (var j = 0; j < sy; j++){
						//blank map with borders
						line.push(i == 0 || i == sx - 1 || j == 0 ||  j == sy - 1 ? 1 : 2);	
					}
					map.push(line);
				}
				
				var maxCells = 300;
				var cellsCounter = 0;
				
				var nextCells = function (point){
					
					
					/*var calculateNeibors = function (x, y){
						var sum = 0;
						for (var i = x-1; i < x + 2; i++){
							for (var j = y-1; j < y + 2; j++){
								sum += isInBounds(map, {x:i,y:j})? map[i][j] : 0; 
							}
						}	
						return sum;
					}*/	
					
					var nextPoint = function(x, y){
						//var cn = calculateNeibors(x,y);
						return {
								valid: !cont(getByPoint(map, {x: point.x + x, y: point.y + y}), [0,1]), 
								point: {x: point.x + x, y: point.y + y},
								i: Math.round(Math.random() * 10)
							};
					}
					
					var n = nextPoint(-1,0);
					var s = nextPoint(1,0);
					var w = nextPoint(0,-1);
					var e = nextPoint(0,1);
					
					var avaliables = [];
					if (n.valid) avaliables.push(n); 
					if (s.valid) avaliables.push(s); 
					if (w.valid) avaliables.push(w); 
					if (e.valid) avaliables.push(e);

					if (avaliables.length > 0){
						avaliables.sort(function (a, b){
							return a.i < b.i ? -1 : 1;
						});	
						return avaliables;
					}
					return false;
				};
				
				var gnawCell = function(point){
					if (cellsCounter < maxCells){
						setByPoint(map, point, 0);		
						cellsCounter++;
					
						var points = nextCells(point);
						forEach(points, function(next){
							if (next){							
								gnawCell(next.point);
							}	
						});
					}
				};
				
				gnawCell({x:3, y:3});
		
				
				return map;	
			}
			 
			function  drawMap(map){
				forEach(map, function(line, i){
					forEach(line, function(cell, j){
						var color = cell == 1 ? "#444" : 
									cell == 2 ? "#888" : 
									cell == 0 ? "#FFF" : 
												"#000"; 	
						minimap.rect(i * 8,j * 8, 8,8).attr({fill:color, stroke:color}); 
					});
				});
			}
			
function wallDistance(theta,map){
    var data=[];
    face=[];

    var x = playerPos[0], y = playerPos[1],
        deltaX, deltaY,
        distX, distY,
        stepX, stepY,
        mapX, mapY;
    
    var atX=Math.floor(x), atY=Math.floor(y);

    var thisRow=-1;
    var thisSide=-1;

    var lastHeight=0;

    for (var i=0; i<samples; i++) {
        theta+=pi/(3*samples)+2*pi;
        theta%=2*pi;

        mapX = atX;
        mapY = atY;

        deltaX=1/Math.cos(theta);
        deltaY=1/Math.sin(theta);

        if (deltaX>0) {
            stepX = 1;
            distX = (mapX + 1 - x) * deltaX;
        }
        else {
            stepX = -1;
            distX = (x - mapX) * (deltaX*=-1);      
        }
        if (deltaY>0) {
            stepY = 1;
            distY = (mapY + 1 - y) * deltaY;
        }
        else {
            stepY = -1;
            distY = (y - mapY) * (deltaY*=-1);
        }
        

        for (var j=0; j<20; j++) {
            if (distX < distY) {
                mapX += stepX;
                if (map[mapX][mapY]) {
                    if (thisRow!=mapX || thisSide!=0) {
                        if (i>0) {
                            data.push(i);
                            data.push(lastHeight);
                        }
                        data.push(i);
                        data.push(distX);
                        thisSide=0;
                        thisRow=mapX;
                        face.push(1+stepX);
                    }
                    lastHeight=distX;
                    break;
                }
                distX += deltaX;
            }
            else {
                mapY += stepY;
                if (map[mapX][mapY]) {
                    if (thisRow!=mapY || thisSide!=1) {
                        if (i>0) {
                            data.push(i);
                            data.push(lastHeight);
                        }   
                        data.push(i);
                        data.push(distY);
                        thisSide=1;
                        thisRow=mapY;
                        face.push(2+stepY);
                    }
                    lastHeight=distY;
                    break;
                }
                distY += deltaY;
            }
        }
    }
    data.push(i);
    data.push(lastHeight);
    
    return data;
}

function drawCanvas(map){

    // var i = walls.length;
    // while (i--) walls[i].hide();
    var theta = playerDir-pi/6;

    var wall=wallDistance(theta, map);

    var linGrad;
    
    var tl,tr,bl,br;
    
    var theta1,theta2,fix1,fix2,j = 0,
        mapview = ["M", 8*playerPos[0], 8*playerPos[1], "L"];
    for (var i=0; i<wall.length; i+=4) {

        theta1=playerDir-pi/6 + pi*wall[i]/(3*samples);
        theta2=playerDir-pi/6 + pi*wall[i+2]/(3*samples);
        
        fix1 = Math.cos(theta1-playerDir);
        fix2 = Math.cos(theta2-playerDir);

        var h=2-playerPosZ;

        var wallH1=100/(wall[i+1]*fix1);
        var wallH2=100/(wall[i+3]*fix2);

        tl=[wall[i]*2 + .001, 150-wallH1*h];
        tr=[wall[i+2]*2 + .001, 150-wallH2*h];
        br=[wall[i+2]*2 + .001, tr[1]+wallH2*2];
        bl=[wall[i]*2 + .001, tl[1]+wallH1*2];

        var shade1=Math.floor(wallH1*2+20); if (shade1>255) shade1=255;
        var shade2=Math.floor(wallH2*2+20); if (shade2>255) shade2=255;

        var c1 = 'rgb('+(face[i/4]%2==0 ? shade1 : 0)+','+(face[i/4]==1 ? shade1 : 0)+','+(face[i/4]==2 ? 0 : shade1)+')';
        var c2 = 'rgb('+(face[i/4]%2==0 ? shade2 : 0)+','+(face[i/4]==1 ? shade2 : 0)+','+(face[i/4]==2 ? 0 : shade2)+')';

        !walls[j] && (walls[j] = canvas.path().attr({stroke: "none", fill: "#000"})).insertBefore(overlay);
        walls[j++].show().attr({
            path: ["M", tl, "L", tr, br, bl],
            fill: "0-" + c1 + "-" + c2
        });
        mapview = mapview.concat([playerPos[0]*8+Math.cos(theta1)*(wall[i+1])*8, playerPos[1]*8+Math.sin(theta1)*(wall[i+1])*8, playerPos[0]*8+Math.cos(theta2)*(wall[i+3])*8, playerPos[1]*8+Math.sin(theta2)*(wall[i+3])*8]);
        
    }
    if (!mapball) {
        mapball = minimap.circle(playerPos[0]*8, playerPos[1]*8, 3).attr({fill:"#36c", stroke: "none"});
        maplight = minimap.path(mapview).attr({fill:"#f00", stroke: "none"});
    } else {
        mapball.attr({
            cx: playerPos[0] * 8,
            cy: playerPos[1] * 8
        });
        maplight.attr({path: mapview});
    }
    while (walls[j]) walls[j++].hide();
}
var mapball, maplight, walls = [];


function nearWall(x,y, map){
    var xx,yy;
    if (isNaN(x)) x=playerPos[0];
    if (isNaN(y)) y=playerPos[1];
    for (var i=-0.1; i<=0.1; i+=0.2) {
        xx=Math.floor(x+i);
        for (var j=-0.1; j<=0.1; j+=0.2) {
            yy=Math.floor(y+j);
            if (map[xx][yy]) return true;
        }
    }
    return false;
}

function update(map){

    total++;

    var change=false;

    if (jumpCycle) {
        jumpCycle--;
        change=true;
        playerPosZ = 1 + jumpCycle*(20-jumpCycle)/110;
    }
    else if (key[4]) jumpCycle=20;
    
    if (key[0]) {
        if (!key[1]) {
            playerDir-=0.07; //left
            change=true;
        }
    }
    else if (key[1]) {
        playerDir+=0.07; //right
        change=true;
    }

    if (change) {
        playerDir+=2*pi;
        playerDir%=2*pi;
        bigsky.attr({x: 1-playerDir/(2*pi)*2400});
        bigsky2.attr({x: 2399 + 1-playerDir/(2*pi)*2400});
    }

    if (key[2] && !key[3]) {
        if (playerVelY<0.1) playerVelY += 0.02;
    }
    else if (key[3] && !key[2]) {
        if (playerVelY>-0.1) playerVelY -= 0.02;
    }
    else {
        if (playerVelY<-0.02) playerVelY += 0.015;
        else if (playerVelY>0.02) playerVelY -= 0.015;
        else playerVelY=0;
    }
    
    
    if (playerVelY!=0) {

        var oldX=playerPos[0];;
        var oldY=playerPos[1];      
        var newX=oldX+Math.cos(playerDir)*playerVelY;
        var newY=oldY+Math.sin(playerDir)*playerVelY;

        if (!nearWall(newX, oldY, map)) {
            playerPos[0]=newX;
            oldX=newX;
            change=true;
        }
        if (!nearWall(oldX, newY, map)) {
            playerPos[1]=newY;
            change=true;
        }

    }
    
    if (playerVelY) wobbleGun();
    if (change) drawCanvas(map);

}


function changeKey(which, to){
    switch (which){
        case 65:case 37: key[0]=to; break; // left
        case 87: case 38: key[2]=to; break; // up
        case 68: case 39: key[1]=to; break; // right
        case 83: case 40: key[3]=to; break;// down
        case 32: key[4]=to; break; // space bar;
        case 17: key[5]=to; break; // ctrl
    }
}
document.onkeydown=function(e){changeKey((e||window.event).keyCode, 1);};
document.onkeyup=function(e){changeKey((e||window.event).keyCode, 0);};
function wobbleGun(){
    var mag=playerVelY;
    overlay.attr({
        x: 10+Math.cos(total/6.23)*mag*90,
        y: 10+Math.cos(total/5)*mag*90
    });
    // document.getElementById("overlay").style.backgroundPosition=(10+Math.cos(total/6.23)*mag*90)+"px "+(10+Math.cos(total/5)*mag*90)+"px";
}


var playerPos=[3.5,3.5]; // x,y (from top left)
var playerDir=0.4; // theta, facing right=0=2pi
var playerPosZ=1;
var samples=400;	
var key=[0,0,0,0,0]; // left, right, up, down
var playerVelY=0;		
var arenaSize=21;
var total=0;
var jumpCycle=0;
var overlay,bigsky,bigsky2;			
			window.onload=function(){
				minimap=Raphael($("minimap"),210,210);
				var map = createMap(21, 21);
				drawMap(map);
				canvas=Raphael($("canvas"),800,600);
				overlay = canvas.image("http://raphaeljs.com/scape/overlay.gif2", 0, 0, 457, 351);
				bigsky = canvas.image("http://raphaeljs.com/scape/sky.jpg1", 0, 0, 2400, 150);
				bigsky2 = canvas.image("http://raphaeljs.com/scape/sky.jpg2", 0, 0, 2400, 150);
				//canvas.rect(0, 150, 400, 150).attr({stroke: "none", fill: "270-#232-#999"});				
				drawCanvas(map);
				bigsky.attr({x: -playerDir/(2*pi)*2400});
				bigsky2.attr({x: 2399 - playerDir/(2*pi)*2400});
				setInterval(function(){update(map);}, 35);
			};
		</script>
	</head>
	<body>
		<table>
			<tr>
				<td><div id="minimap"></div></td>
				<td><DIV id="canvas" width="400" height="300"></DIV></td>
			</tr>
		</table>	
	</body>
</html>